package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.apis.wemedia.IWemediaClient;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.HotArticleService;
import com.heima.article.service.RedisLuaService;
import com.heima.common.constant.ArticleConstant;
import com.heima.common.constant.GlobalConstant;
import com.heima.common.exception.CustomException;
import com.heima.model.article.dots.UpdateArticleMsg;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    ApArticleService apArticleService;
    @Autowired
    IWemediaClient iWemediaClient;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedisLuaService redisLuaService;
    @Override
    public void updateHotArticle(UpdateArticleMsg msg)
    {
        if(msg==null)
        {
            throw new CustomException(AppHttpCodeEnum.PARAMS_NOT_NULL);
        }
        UpdateArticleMsg.getInit(msg);
        ApArticle article = apArticleService.getById(msg.getArticleId());
        if (article==null)
        {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        Integer score=calculateScore(article)+getScoreByMsg(msg);
        article.setLikes(article.getLikes()+msg.getLikes());
        article.setViews(article.getViews()+msg.getViews());
        article.setCollection(article.getCollection()+msg.getCollection());
        article.setComment(article.getComment()+msg.getComment());
        apArticleService.updateById(article);
        String allKey=GlobalConstant.HOT_TAG+ArticleConstant.DEFAULT_TAG;
        String key=GlobalConstant.HOT_TAG+article.getChannelId();
        HotArticleVo hotArticleVo = getHotArticleVo(article, score);
        addToHot(key,hotArticleVo);
        log.info("add to hot,key->{}",key);
        addToHot(allKey,hotArticleVo);
        log.info("add to hot,allkey->{}",allKey);
    }
    private void addToHot(String key,HotArticleVo hotArticleVo)
    {
        log.info("hot article vo->{}",hotArticleVo);
        List<String> list = stringRedisTemplate.opsForList().range(key, 0, -1);
        List<HotArticleVo> hotArticleVoList = list.stream().map(item -> JSON.parseObject(item, HotArticleVo.class)).collect(Collectors.toList());
        List<Long> ids = hotArticleVoList.stream().map(item -> item.getId()).collect(Collectors.toList());
        if(ids.contains(hotArticleVo.getId()))
        {
            //TODO 折半法
            for (int i=0;i<hotArticleVoList.size();i++) {
                HotArticleVo vo = hotArticleVoList.get(i);
                if (vo.getId().equals(hotArticleVo.getId()))
                {
                    vo.setScore(hotArticleVo.getScore());
                    log.info("replace score");
                    break;
                }
            }
            Collections.sort(hotArticleVoList, new Comparator<HotArticleVo>() {
                @Override
                public int compare(HotArticleVo o1, HotArticleVo o2) {
                    return o2.getScore()-o1.getScore();
                }
            });
        }
        else{
            //TODO 折半法
            int i=hotArticleVoList.size()-1;
            for(;i>=0;i--)
            {
                HotArticleVo vo = hotArticleVoList.get(i);
                if(vo.getScore()>hotArticleVo.getScore())
                {
                    hotArticleVoList.add(i+1,hotArticleVo);
                    break;
                }
            }
            if(i<0)
            {
                hotArticleVoList.add(i+1,hotArticleVo);
            }
            log.info("hot article insert :{}",i+1);
            if(hotArticleVoList.size()>GlobalConstant.SCORE_LIMIT)
            {
                hotArticleVoList.remove(GlobalConstant.SCORE_LIMIT);
            }
        }
        list = hotArticleVoList.stream().map(item -> JSON.toJSONString(item)).collect(Collectors.toList());
        redisLuaService.replaceList(key,list);
    }
    @Override
    @XxlJob(GlobalConstant.XXL_JOB_HOT_SCHEDULE)
    public void calculateHotArticle() {
        log.info("算分开始》》》");
        ResponseResult responseResult = iWemediaClient.getAllChannel();
        if (responseResult==null||!responseResult.getCode().equals(GlobalConstant.OK_CODE)||responseResult.getData()==null)
        {
            throw new CustomException(AppHttpCodeEnum.FEIGN_FAIL);
        }
        String jsonString = JSON.toJSONString(responseResult.getData());
        List<WmChannel> wmChannels = JSON.parseArray(jsonString, WmChannel.class);
        for (WmChannel wmChannel : wmChannels) {
            String key=GlobalConstant.HOT_TAG+wmChannel.getId();
            stringRedisTemplate.delete(key);
        }
        stringRedisTemplate.delete(GlobalConstant.HOT_TAG+ArticleConstant.DEFAULT_TAG);
        LocalDateTime dateTime=LocalDateTime.now().plusDays(-GlobalConstant.SCORE_DATE);
        List<ApArticle> list = apArticleService.list(Wrappers.<ApArticle>lambdaQuery().ge(ApArticle::getPublishTime, dateTime));
        Map<String, List<HotArticleVo>> map = calculateScore(list);
        Set<Map.Entry<String , List<HotArticleVo>>> entries = map.entrySet();
        for (Map.Entry<String, List<HotArticleVo>> entry : entries) {
            List<String> collect = entry.getValue().stream().sorted(new Comparator<HotArticleVo>() {
                @Override
                public int compare(HotArticleVo o1, HotArticleVo o2) {
                    return o2.getScore() - o1.getScore();
                }
            }).limit(GlobalConstant.SCORE_LIMIT).map(item->JSON.toJSONString(item)).collect(Collectors.toList());
            String key=GlobalConstant.HOT_TAG+entry.getKey();
            redisLuaService.replaceList(key,collect);
        }
        log.info("算分结束《《《");
    }
    private Map<String, List<HotArticleVo>> calculateScore(List<ApArticle> list)
    {
        List<HotArticleVo> hotArticleVoList = list.stream().map(item -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(item, hotArticleVo);
            hotArticleVo.setScore(calculateScore(item));
            return hotArticleVo;
        }).collect(Collectors.toList());

        Map<String, List<HotArticleVo>> collect = hotArticleVoList.stream().collect(Collectors.groupingBy(item->item.getChannelId().toString()));
        collect.put(ArticleConstant.DEFAULT_TAG,hotArticleVoList);
        return collect;
    }
    private Integer calculateScore(ApArticle article)
    {
        Integer score = article.getLikes() * GlobalConstant.LIKE_WEIGHT
                + article.getViews() * GlobalConstant.VIEW_WEIGHT
                + article.getComment() * GlobalConstant.COMMENT_WEIGHT
                + article.getCollection() * GlobalConstant.COLLECT_WEIGHT;
        return score;
    }
    private Integer getScoreByMsg(UpdateArticleMsg msg)
    {
        Integer score = msg.getLikes() * GlobalConstant.LIKE_WEIGHT
                + msg.getViews() * GlobalConstant.VIEW_WEIGHT
                + msg.getComment() * GlobalConstant.COMMENT_WEIGHT
                + msg.getCollection() * GlobalConstant.COLLECT_WEIGHT;
        return score*GlobalConstant.SCORE_MUL_POWER;
    }
    private HotArticleVo getHotArticleVo(ApArticle article,Integer score)
    {
        HotArticleVo hotArticleVo = new HotArticleVo();
        BeanUtils.copyProperties(article,hotArticleVo);
        hotArticleVo.setScore(score);
        log.info("hot article id->{}",hotArticleVo.getId());
        return hotArticleVo;
    }
}
